package org.jeecg.modules.cosmetic.handler;

import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.modules.cosmetic.constant.enums.IsDeleteEnum;
import org.jeecg.modules.cosmetic.entity.*;
import org.jeecg.modules.cosmetic.model.dto.FormulaJsonDTO;
import org.jeecg.modules.cosmetic.model.dto.IngredientProductDTO;
import org.jeecg.modules.cosmetic.model.dto.TIngredientResultDTO;
import org.jeecg.modules.cosmetic.model.vo.FormulaExcelVO;
import org.jeecg.modules.cosmetic.model.vo.IngredientComparatorVO;
import org.jeecg.modules.cosmetic.service.*;
import org.jeecg.modules.cosmetic.utils.PackageUtil;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
@Slf4j
public class IngredientReviewHandler {

    @Resource
    private ITIngredientProductService ingredientProductService;

    @Resource
    private ITIngredientResultService ingredientResultService;

    @Resource
    private ITIngredientReviewService itIngredientReviewService;

    @Resource
    private ITCheckFormulaService checkFormulaService;
    /**
     * 导入配方-(根据产品主键查询配方)
     * @param productId
     * @return
     */
    @Transactional
    @DS("cosmetic-datasource")
    public Result<IngredientProductDTO> queryFormulaReviewList(Long productId) throws Exception {

        TCheckFormula checkFormula = checkFormulaService.getById(productId);
        Optional.ofNullable(checkFormula).orElseThrow(() -> new JeecgBootException("产品信息不存在"));
        if (Objects.equals(checkFormula.getStatus(), IsDeleteEnum.DELETE.getCode())) {
            log.error("当前产品已删除，id：{}", productId);
            return Result.error("产品信息不存在");
        }
        if (StringUtils.isEmpty(checkFormula.getInputJson())) {
            return Result.error("当前料物质信息为空");
        }
        List<FormulaJsonDTO> formulaJsonDTOS = JSON.parseArray(checkFormula.getInputJson(), FormulaJsonDTO.class);
        if (CollectionUtils.isEmpty(formulaJsonDTOS)) {
            throw new JeecgBootException("配方成分信息JSON解析异常");
        }

        // 保存虚拟产品数据和对应的配方信息
        TIngredientProduct tIngredientProduct = saveIngredientProduct();
        saveIngredientBatch(formulaJsonDTOS, tIngredientProduct);
        return Result.OK(IngredientProductDTO.builder().productId(tIngredientProduct.getId()).build());
    }

    /**
     * 导入配方-(通过Excel文件)
     * @param multipartRequest
     * @return
     * @throws Exception
     */
    @Transactional
    @DS("cosmetic-datasource")
    public Result<IngredientProductDTO> importFormulaExcel(MultipartHttpServletRequest multipartRequest) throws Exception {
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            // 解析Excel 文件
            MultipartFile file = entity.getValue();
            ImportParams params = new ImportParams();
            params.setTitleRows(1);
            params.setHeadRows(1);
            params.setNeedSave(true);
            List<FormulaExcelVO> formulaExcelVOS = ExcelImportUtil.importExcel(file.getInputStream(), FormulaExcelVO.class, params);
            checkExcelParams(formulaExcelVOS);

            // 保存虚拟产品数据信息
            TIngredientProduct tIngredientProduct = saveIngredientProduct();

            // 再把数据存入对应的结果表
            // 先合并重复的数据
            List<FormulaExcelVO> formulaExcelVOList = new ArrayList<>(formulaExcelVOS.stream().collect(Collectors.toMap(FormulaExcelVO::getIncicName, a -> a, (s1, s2) -> {
                BigDecimal actualContent = new BigDecimal(s1.getActualContent()).add(new BigDecimal(s2.getActualContent()));
                s1.setActualContent(String.valueOf(actualContent));
                return s1;
            })).values());
            List<TIngredientResult> resultList = Lists.newArrayList();
            IntStream.range(0, formulaExcelVOList.size()).forEach(i -> {
                formulaExcelVOList.sort(new IngredientComparatorVO());
                TIngredientResult ingredientResult = TIngredientResult.builder()
                        .productId(tIngredientProduct.getId())
                        .standardNum(i + 1)
                        .standardInci(formulaExcelVOList.get(i).getIncicName())
                        .standardContent(formulaExcelVOList.get(i).getActualContent()).build();
                resultList.add(ingredientResult);
            });
            ingredientResultService.saveBatch(resultList);
            return Result.OK(IngredientProductDTO.builder().productId(tIngredientProduct.getId()).build());
        }
        return Result.error("导入配方模版失败");
    }

    /**
     * 成分审核提交方法
     * @param message
     * @param productId
     */
    @Transactional
    @DS("cosmetic-datasource")
    public Result<List<TIngredientResultDTO>> submitReview(String message, Long productId) {
        if (StringUtils.isBlank(message)) {
            log.error("录入的成分信息为空");
            return Result.error("录入的成分信息为空");
        }
        Optional.ofNullable(productId).orElseThrow(() -> new JeecgBootException("导入的标准配方数据为空"));

        AtomicInteger integer = new AtomicInteger(0);

        List<TIngredientResult> list2;
        List<TIngredientResult> list3 = Lists.newArrayList();

        // 先判断是中文还是英文，如果是英文，那就以英文的逗号去解析字符
        // 如果是中文，那么就以中文的顿号去解析字符
        // 中文
        if (PackageUtil.hasChinese(message)) {
            list2 = Arrays.stream(message.split("、")).map(m -> {
                return TIngredientResult.builder().ingredientNum(Integer.toString(integer.incrementAndGet())).ingredientInci(m).build();
            }).collect(Collectors.toList());
        } else {
            list2 = Arrays.stream(message.split(",")).map(m -> {
                return TIngredientResult.builder().ingredientNum(Integer.toString(integer.incrementAndGet())).ingredientInci(m).build();
            }).collect(Collectors.toList());
        }

        // 获取标准配方数据
        List<TIngredientResult> list1 = ingredientResultService.list(new LambdaQueryWrapper<TIngredientResult>().eq(TIngredientResult::getProductId, productId).orderByAsc(TIngredientResult::getStandardNum));
        if (CollectionUtils.isEmpty(list1)) {
            log.error("导入的标准配方数据为空");
            return Result.error("导入的标准配方数据为空");
        }
        list1.forEach(items -> items.setResult(null));

        // 先比较相等和拼写错误的数据
        compareGreaterOrLessList(list1, list2, list3, list1.size() >= list2.size());

        // 比较顺序错误的数据
        List<TIngredientResult> resultList1 = list1.stream().filter(items -> items.getResult() == null).collect(Collectors.toList());
        List<TIngredientResult> resultList2 = list2.stream().filter(items -> items.getResult() == null).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(resultList1) && !CollectionUtils.isEmpty(resultList2)) {
            compareQueue(resultList1, resultList2, list3);
        }

        // 比较原料缺失数据
        // 如果集合为空，在没有原料缺失的情况
        List<TIngredientResult> resultList1_1 = list1.stream().filter(items -> items.getResult() == null).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(resultList1_1)) {
            resultList1_1.forEach(items -> {
                items.setIngredientNum("/");
                items.setIngredientInci("/");
                items.setResult("原料缺失");
            });
            list3.addAll(resultList1_1);
        }

        // 比较新增数据
        // 如果集合为空，在没有新增的情况
        List<TIngredientResult> resultList2_2 = list2.stream().filter(items -> items.getResult() == null).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(resultList2_2)) {
            resultList2_2.forEach(items -> items.setResult("新增"));
            list3.addAll(resultList2_2);
        }

        // 把输入的配方信息和比较结果存到数据库
        saveReviewOrResult(message, list3);

        // 封装结果数据，并把新增的数据放在最后位置显示
        list3.sort(Comparator.comparing(TIngredientResult::getStandardNum, Comparator.nullsLast(Integer::compareTo)));
        return Result.OK(list3.stream().map(m -> {
            return TIngredientResultDTO.builder()
                    .standardNum(m.getStandardNum())
                    .standardInci(m.getStandardInci())
                    .standardContent(m.getStandardContent())
                    .ingredientNum(m.getIngredientNum())
                    .ingredientInci(m.getIngredientInci())
                    .result(m.getResult())
                    .build();
        }).collect(Collectors.toList()));
    }

    /**
     * 保存和更新配方信息数据
     * @param message
     * @param list3
     */
    private void saveReviewOrResult(String message, List<TIngredientResult> list3) {
        // 先保存主表数据
        TIngredientReview tIngredientReview = TIngredientReview.builder().message(message).build();
        itIngredientReviewService.save(tIngredientReview);

        // 更新之前已经存在的数据
        ingredientResultService.updateBatchById(list3.stream().filter(items -> items.getStandardNum() != null).map(item -> item.setRevId(tIngredientReview.getId())).collect(Collectors.toList()));

        // 新增的数据要做保存操作
        ingredientResultService.saveBatch(list3.stream().filter(items -> items.getStandardNum() == null).map(item -> item.setRevId(tIngredientReview.getId())).collect(Collectors.toList()));
    }

    /**
     * 比较顺序错误的数据
     * @param resultList1 标准配方数据
     * @param resultList2 录入的数据
     * @param list3 存放比较结果
     */
    private void compareQueue(List<TIngredientResult> resultList1, List<TIngredientResult> resultList2, List<TIngredientResult> list3) {
        resultList2.forEach(items -> {
            resultList1.forEach(item -> {
                if (items.getIngredientInci().equals(item.getStandardInci())) {
                    if (!items.getIngredientNum().equals(item.getStandardNum().toString())) {
                        // 顺序错误
                        item.setIngredientNum(items.getIngredientNum());
                        item.setIngredientInci(items.getIngredientInci());
                        item.setResult("顺序错误");
                        list3.add(item);
                        items.setResult("顺序错误");
                    }
                }
            });
        });
    }

    /**
     * 当标准配方数据大于录入配方数据时
     * 当标准配方数据小于录入配方数据时
     * 比较相等数据和拼写错误数据
     * @param list1 标准配方数据
     * @param list2 录入的数据
     * @param list3 存放比较结果
     */
    private void compareGreaterOrLessList(List<TIngredientResult> list1, List<TIngredientResult> list2, List<TIngredientResult> list3, Boolean b ) {
        int length1 = 0;
        int length2 = 0;
        if (b) {
            length1 = list1.size();
            length2 = list2.size();
        } else {
            length1 = list2.size();
            length2 = list1.size();
        }
        int finalLength = length2;
        IntStream.range(0, length1).allMatch(i -> {
            if (i+1 > finalLength) {
                return false;
            }
            if (list2.get(i).getIngredientInci().equals(list1.get(i).getStandardInci())) {
                // 相等
                list1.get(i).setIngredientNum(list2.get(i).getIngredientNum());
                list1.get(i).setIngredientInci(list2.get(i).getIngredientInci());
                list1.get(i).setResult("");
                list3.add(list1.get(i));
                list2.get(i).setResult("");
            } else if (PackageUtil.SimilarityWord(list2.get(i).getIngredientInci(), list1.get(i).getStandardInci()) <= 3) {
                // 拼写错误
                list1.get(i).setIngredientNum(list2.get(i).getIngredientNum());
                list1.get(i).setIngredientInci(list2.get(i).getIngredientInci());
                list1.get(i).setResult("拼写错误");
                list3.add(list1.get(i));
                list2.get(i).setResult("拼写错误");
            }
            return true;
        });
    }

    /**
     * 保存对应结果数据
     * @param formulaList
     * @param tIngredientProduct
     * @return
     */
    private void saveIngredientBatch(List<FormulaJsonDTO> formulaList, TIngredientProduct tIngredientProduct) throws Exception{
        List<TIngredientResult> resultList = Lists.newArrayList();
        IntStream.range(0, formulaList.size()).forEach(i -> {
            TIngredientResult ingredientResult = TIngredientResult.builder()
                    .productId(tIngredientProduct.getId())
                    .standardNum(i + 1)
                    .standardInci(formulaList.get(i).getIncic_name())
                    .standardContent(formulaList.get(i).getInput_value()).build();
            resultList.add(ingredientResult);
        });
        ingredientResultService.saveBatch(resultList);
    }

    /**
     * 保存虚拟产品数据
     * @return
     */
    private TIngredientProduct saveIngredientProduct() {
        TIngredientProduct tIngredientProduct = new TIngredientProduct();
        TIngredientProduct product = ingredientProductService.getOne(new LambdaQueryWrapper<TIngredientProduct>().orderByDesc(TIngredientProduct::getId).last("limit 1"));
        if (ObjectUtils.isEmpty(product)) {
            tIngredientProduct.setName("虚拟产品" + 1);
            tIngredientProduct.setCode("00" + 1);
        } else {
            tIngredientProduct.setName("虚拟产品" + (product.getId() + 1L));
            tIngredientProduct.setCode("00" + (product.getId() + 1L));
        }
        ingredientProductService.save(tIngredientProduct);
        return tIngredientProduct;
    }

    /**
     * @param formulaExcelVOS
     * @description excel参数校验
     */
    private void checkExcelParams(List<FormulaExcelVO> formulaExcelVOS) {
        for (int i = 0; i < formulaExcelVOS.size(); i++) {
            int lineNumber = i + 1;
            FormulaExcelVO formulaExcelVO = new FormulaExcelVO();
            BeanUtils.copyProperties(formulaExcelVOS.get(i), formulaExcelVO);
            if (StringUtils.isEmpty(formulaExcelVO.getCnName())) {
                throw new JeecgBootException("第 " + lineNumber + " 行：标准中文名称为空。");
            }
            if (StringUtils.isEmpty(formulaExcelVO.getIncicName())) {
                throw new JeecgBootException("第 " + lineNumber + " 行：INCIC名称/英文名称为空。");
            }
            if (StringUtils.isEmpty(formulaExcelVO.getIngredientContent())) {
                throw new JeecgBootException("第 " + lineNumber + " 行：原料中成分含量为空。");
            }
            if (StringUtils.isEmpty(formulaExcelVO.getActualContent())) {
                throw new JeecgBootException("第 " + lineNumber + " 行：实际成分含量为空。");
            }
        }
    }
}
